import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/bloc.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/account_page_model.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/home_page_model.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/signing_in_page_model.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/signing_up_page_model.dart';
import 'package:website.core/core/application/blocs/app_navigator_bloc/models/page_models/web_purchase_item_page_model.dart';
import 'package:website.core/core/application/blocs/auth_bloc/bloc.dart';
import 'package:website.core/core/application/blocs/url_launcher_bloc/bloc.dart';
import 'package:website.core/core/domain/config/config.dart';
import 'package:website.core/core/domain/entities/user.dart';
import 'package:website.core/core/domain/use_cases/sign_out/use_case.dart';
import 'package:website.core/core/domain/utils/either_extension.dart';
import 'package:website.home/web_purchase_item_page/domain/use_cases/get_web_purchase_item_models/models/web_purchase_item_models.dart';
import 'package:website.home/web_purchase_item_page/domain/use_cases/get_web_purchase_item_models/use_case.dart';
import 'package:website.home/web_purchase_item_page/domain/use_cases/place_order/use_case.dart';

part 'bloc_state.dart';

class WebPurchaseItemPageBloc extends Cubit<WebPurchaseItemPageBlocState> {
  WebPurchaseItemPageBloc({
    required UrlLauncherBloc urlLauncherBloc,
    required AuthBloc authBloc,
    required AppNavigatorBloc appNavigatorBloc,
    required GetWebPurchaseItemModels getWebPurchaseItemModels,
    required PlaceOrder placeOrder,
    required SignOut signOut,
  })  : _urlLauncherBloc = urlLauncherBloc,
        _authBloc = authBloc,
        _appNavigatorBloc = appNavigatorBloc,
        _getWebPurchaseItemModels = getWebPurchaseItemModels,
        _placeOrder = placeOrder,
        _signOut = signOut,
        super(_getInitialState(
          authBlocState: authBloc.state as InitializedAuthBlocState,
        )) {
    _authBlocSubscription = authBloc.stream.listen((authBlocState) {
      _onAuthBlocStateChanged(authBlocState as InitializedAuthBlocState);
    });
  }

  final UrlLauncherBloc _urlLauncherBloc;
  final AuthBloc _authBloc;
  final AppNavigatorBloc _appNavigatorBloc;

  final GetWebPurchaseItemModels _getWebPurchaseItemModels;
  final PlaceOrder _placeOrder;
  final SignOut _signOut;

  late final StreamSubscription<AuthBlocState> _authBlocSubscription;

  static WebPurchaseItemPageBlocState _getInitialState({
    required InitializedAuthBlocState authBlocState,
  }) {
    User? user;

    if (authBlocState is AuthorizedAuthBlocState) {
      user = authBlocState.user;
    }

    return InitialWebPurchaseItemPageBlocState(
      user: user,
    );
  }

  void load() {
    if (state is! InitialWebPurchaseItemPageBlocState) {
      return;
    }

    emit(LoadingWebPurchaseItemPageBlocState(
      user: state.user,
    ));

    _runLoading();
  }

  void _runLoading() async {
    final failureOrResult = await _getWebPurchaseItemModels();

    if (failureOrResult.isLeft()) {
      Future.delayed(const Duration(seconds: 2), _runLoading);
      return;
    }

    final result = failureOrResult.asRight();

    emit(LoadedWebPurchaseItemPageBlocState(
      user: state.user,
      monthSubscriptionModel: result.monthSubscriptionModel,
      yearSubscriptionModel: result.yearSubscriptionModel,
      lifetimePurchaseItemModel: result.lifetimePurchaseItemModel,
      selectedPurchaseItemSku: result.yearSubscriptionModel.sku,
      purchaseRequestInProgress: false,
      orderPlacingFailure: null,
    ));
  }

  void selectPurchaseItem({
    required String purchaseItemSku,
  }) {
    final _state = state;

    if (_state is! LoadedWebPurchaseItemPageBlocState) {
      return;
    }

    emit(_state.copyWith(
      selectedPurchaseItemSku: () => purchaseItemSku,
    ));
  }

  void requestPurchase() async {
    final _state = state;

    if (_state is! LoadedWebPurchaseItemPageBlocState) {
      return;
    }

    if (_authBloc.state is AuthorizedAuthBlocState) {
      final returnUrl = Config().websiteBaseUrl;

      final failureOrResult = await _placeOrder(
        purchaseItemSku: _state.selectedPurchaseItemSku,
        returnUrl: returnUrl,
      );

      if (failureOrResult.isLeft()) {
        final failure = failureOrResult.asLeft();

        if (failure is UnauthorizedOrderPlacingFailure) {
          return;
        }

        if (failure is NetworkOrderPlacingFailure || failure is ServerOrderPlacingFailure) {
          emit(_state.copyWith(
            orderPlacingFailure: () => failure,
          ));

          return;
        }

        throw UnimplementedError();
      }

      final result = failureOrResult.asRight();

      _urlLauncherBloc.launch(
        url: result.confirmationUrl,
      );
    } else {
      final pageModel = SigningUpPageModel(
        targetPageModel: null,
      );

      _appNavigatorBloc.pushPage(
        pageModel: pageModel,
      );
    }
  }

  void openHomePage() {
    final pageModel = HomePageModel();

    _appNavigatorBloc.pushPageReplacement(
      pageModel: pageModel,
    );
  }

  void openWebPurchaseItemPage() {
    final pageModel = WebPurchaseItemPageModel();

    _appNavigatorBloc.pushPageReplacement(
      pageModel: pageModel,
    );
  }

  void openSigningInPage() {
    final pageModel = SigningInPageModel(
      targetPageModel: null,
    );

    _appNavigatorBloc.pushPage(
      pageModel: pageModel,
    );
  }

  void openSigningUpPage() {
    final pageModel = SigningUpPageModel(
      targetPageModel: null,
    );

    _appNavigatorBloc.pushPage(
      pageModel: pageModel,
    );
  }

  void openAccountPage() {
    final pageModel = AccountPageModel(
      fragmentModel: const AccountOverviewAccountPageFragmentModel(),
    );

    _appNavigatorBloc.pushPageReplacement(
      pageModel: pageModel,
    );
  }

  void requestSigningOut() {
    final authBlocState = _authBloc.state;

    if (authBlocState is! AuthorizedAuthBlocState) {
      return;
    }

    final refreshToken = authBlocState.accessCredentials.refreshToken;

    unawaited(_signOut(
      refreshToken: refreshToken,
    ));

    _authBloc.setAuthenticationData(
      accessCredentials: () => null,
      user: () => null,
    );
  }

  void _onAuthBlocStateChanged(InitializedAuthBlocState authBlocState) {
    User? user;

    if (authBlocState is AuthorizedAuthBlocState) {
      user = authBlocState.user;
    }

    emit(state.copyWith(
      user: () => user,
    ));
  }

  @override
  Future<void> close() {
    _authBlocSubscription.cancel();
    return super.close();
  }
}
